
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Container;
using Swaf.Security;

namespace Articles
{
	public class ArticleRssBizRules : Syrinx.BaseBizRules
	{
		public IBizObjList getRssLeafNodeOptions(IApplication app, IRuleContext context)
		{
			return app.appData.get("RssLeafNodeOptions") as IBizObjList;
		}

		public IBizObjList getCompanyRssChannels(IApplication app, IRuleContext context, Swaf.BGuid.Guid companyId)
		{
			FlexiMap m = new FlexiMap();
			if (companyId == null)
				companyId = ActiveCompanyId;

			m.put("companyId", companyId);
			IBizObjList feeds = app.appData.get("DA.Articles.GetCompanyRssChannels", m) as IBizObjList;
			return feeds;
		}

		public void updateCompanyRssChannel(IApplication app, IRuleContext context, IBizObj feed)
		{
			if (!feed.isDirty("companyId") || feed.get("companyId") == null)
				feed.put("companyId", ActiveCompanyId);
			if (feed.isNew)
				app.appData.put("DA.Articles.AddRssChannel", feed);
			else
				app.appData.put("DA.Articles.UpdateRssChannel", feed);
		}

		public void deleteCompanyRssChannel(IApplication app, IRuleContext context, Swaf.BGuid.Guid feedId)
		{
			FlexiMap m = new FlexiMap();
			m.put("id", feedId);
			app.appData.put("DA.Articles.DeleteRssChannel", m);
		}

		/// <summary>
		/// This bizrule will generate a string of RSS Xml for all of the articles within the given article id
		/// passed.  
		/// </summary>
		/// <remarks>If a user is not logged into the application when the RSS feed is requested, it will be done
		/// under the anonymous user of the company.  The company used is based on the specific URL used to get
		/// the RSS or the root company if a generic URL was used. For more information on the RSS 2.0
		/// specification that this bizrule uses see http://www.rssboard.org/rss-specification.</remarks>
		/// <param name="title">The title to use for the RSS Xml document</param>
		/// <param name="link">The HTTP link to get this RSS</param>
		/// <param name="description">The description to use for the Rss Xml document</param>
		/// <param name="albumLink">The prefix HTTP URI to use for each album link.</param>
		/// <returns>The RSS Xml</returns>
		public string getArticleRss(IApplication app, IRuleContext context, string rssUniqueId, Uri link)
		{
            try
            {
				FlexiMap m = new FlexiMap();
                string rssXml = "";
                IBizObj rssFeed = null;
                Swaf.BGuid.Guid articleId = Swaf.BGuid.Guid.TryParse(rssUniqueId);
                if (articleId == null)
                    articleId = context.RuleGroup.execute(context, "getArticleIdFromUniqueTitle", rssUniqueId, null).CallResults as Swaf.BGuid.Guid;
                if (articleId != null)
                {
                    m.put("id", articleId);
                    rssFeed = app.appData.get("DA.Articles.GetRssFeed", m) as IBizObj;
                }
                if (rssFeed != null)
                {
                    string baseLoc = link.AbsoluteUri.Substring(0, link.AbsoluteUri.IndexOf(link.AbsolutePath));
                    bool includePodCast = (bool)rssFeed.get("includePodCastAttribs");
                    bool useBodyAsSummary = (bool)rssFeed.get("useBodyAsDescription");
                    bool useLastUpdateAsFeedDate = (bool)rssFeed.get("useLastUpdateAsFeedDate");

                    StringWriter s = new StringWriter();
                    XmlTextWriter xml = new XmlTextWriter(s);
                    xml.WriteStartElement("rss");
                    xml.WriteAttributeString("version", "2.0");
                    if (includePodCast)
                        xml.WriteAttributeString("xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd");
                    xml.WriteStartElement("channel");
                    m.put("BizObjType", "Article");
                    m.put("parentId", articleId);
                    m.put("includeSelf", true);
                    m.put("entireTree", rssFeed.get("entireTree"));
                    m.put("listDetailLevel", useBodyAsSummary ? DetailLevel.fullDetails : DetailLevel.basicDetails);
                    IResults rc = app.ruleMgr.execute("getArticles", m, (IBizObj)null);
                    IBizObjList articles = rc.CallResults as IBizObjList;
                    if (articles != null)
                        for (int pos = 0; pos < articles.numElements; pos++)
                        {
                            IBizObj article = articles[pos];
                            if (pos == 0)
                            {

                                xml.WriteElementString("title", article.get("title").ToString());
                                xml.WriteElementString("link", baseLoc);
                                xml.WriteElementString("description", article.get("summary").ToString());

                                if (rssFeed.get("rootArticle.counts.dateUpdated") != null || rssFeed.get("rootArticle.counts.dateCreated") != null)
                                {
                                    if (useLastUpdateAsFeedDate)
                                        xml.WriteElementString("lastBuildDate", GetRFC822Date((DateTime)rssFeed.get("rootArticle.counts.dateUpdated")));
                                    else
                                        xml.WriteElementString("lastBuildDate", GetRFC822Date((DateTime)rssFeed.get("rootArticle.counts.dateCreated")));
                                }
                                else
                                {
                                    //somehow both fiedls are null so i use current date for rss display
                                    xml.WriteElementString("lastBuildDate", GetRFC822Date(DateTime.Now));
                                }
                                
                                if (rssFeed.get("rootArticle.publishDate") != null )
                                    xml.WriteElementString("pubDate", GetRFC822Date((DateTime)rssFeed.get("rootArticle.publishDate")));

                                string[] keywords = null;
                                if (article.get("keyword") != null)
                                    keywords = article.get("keyword").ToString().Split(' ');

                                if (keywords != null)
                                    for (int i = 0; i < keywords.Length; i++)
                                        xml.WriteElementString("category", keywords[i]);

                                string feedImg = string.Format("{0}/{1}/Thumbnail/{2}",baseLoc, article.get("mediaCacheName"), article.get("primaryPicture.fileName"));
                                xml.WriteStartElement("image");
                                xml.WriteElementString("url", feedImg);
                                xml.WriteElementString("title", article.get("title").ToString());
                                xml.WriteElementString("link", feedImg);
                                xml.WriteEndElement();

                                // add pod cast
                                if (includePodCast)
                                {
                                    xml.WriteElementString("itunes:author", article.get("publishAuthorName").ToString());
                                    xml.WriteElementString("itunes:subtitle", article.get("title").ToString());
                                    xml.WriteElementString("itunes:summary", article.get("summary").ToString());
                                    xml.WriteElementString("itunes:explicit", "No");

                                    xml.WriteStartElement("itunes:owner");
                                    xml.WriteElementString("itunes:name", article.get("publishAuthorName").ToString());
                                    xml.WriteElementString("itunes:email", article.get("counts.lastChildUpdatedBy.contact.email").ToString());
                                    xml.WriteEndElement();

                                    xml.WriteStartElement("itunes:image");
                                    xml.WriteAttributeString("href", feedImg);
                                    xml.WriteEndElement();
                                    xml.WriteStartElement("itunes:category");
                                    xml.WriteAttributeString("text", "Technology");
                                    xml.WriteStartElement("itunes:category");
                                    xml.WriteAttributeString("text", "Podcasting");
                                    xml.WriteEndElement();
                                    xml.WriteEndElement();
                                }
                            }
                            else
                            {
                                if ((Convert.ToInt32(rssFeed.get("leafNodes")) != 0) && pos < articles.numElements - 1)
                                {
                                    IBizObj nextArticle = articles[pos + 1];
                                    int curDepth = (int)article.get("depth");
                                    int nextDepth = (int)nextArticle.get("depth");
                                    if (nextDepth > curDepth)
                                        continue;
                                }
                                string linkguid = app.resMgr.resolve(String.Format("{0}$CmpSetting(sitePath)$/articles/{1}_{2}.aspx", baseLoc, rssUniqueId, article.get("uniqueId")));

                                //devnotes: see LastBuildDate notes above ....
                                DateTime pubDate = DateTime.Now;
                                if (article.get("counts.dateCreated") != null && article.get("counts.dateUpdated") != null)
                                {
                                    if(useLastUpdateAsFeedDate)
                                        pubDate = (DateTime)article.get("counts.dateUpdated");
                                    else
                                        pubDate = (DateTime)article.get("counts.dateCreated");
                                }
                                
                                // use publistDate when available
                                if (article.get("publishDate") != null && ((DateTime)article.get("publishDate") != DateTime.MinValue))
                                    pubDate = (DateTime)article.get("publishDate");
                                string summary = article.get("summary").ToString();
                                if (useBodyAsSummary)
                                    summary = article.get("body").ToString();

                                xml.WriteStartElement("item");
                                xml.WriteElementString("title", article.get("title").ToString());
                                xml.WriteElementString("link", linkguid);
                                xml.WriteElementString("guid", linkguid);
                                xml.WriteElementString("description", summary);
                                xml.WriteElementString("pubDate", GetRFC822Date(pubDate));
                                xml.WriteEndElement();

                                if (includePodCast)
                                {
                                    rc = app.ruleMgr.execute("getArticleAttachmentGroupsWithFiles", article.get("id") as Swaf.BGuid.Guid);
                                    IBizObjList files = rc.CallResults as IBizObjList;
                                    if (files.numElements > 0)
                                    {
                                        
                                        IBizObjList attachments = files[0].get("files") as IBizObjList;
                                        for (int i = 0; i < attachments.numElements; i++)
                                        {
                                            IBizObj file = attachments[i];
                                            mediaType mt = calcMediaType(file.get("filePathName").ToString());
                                            if (file != null && mt != mediaType.NotSupported)
                                            {

                                                xml.WriteStartElement("item");
                                                xml.WriteStartElement("enclosure");
                                                xml.WriteAttributeString("url", string.Format("{0}/{1}", baseLoc, file.get("fileUrl").ToString().Replace(@"\", "/")));
                                                xml.WriteAttributeString("length", calcLength(file.get("size").ToString()));
                                                xml.WriteAttributeString("type",  mt == mediaType.Audio ? "audio/mpeg" : 
                                                    mt == mediaType.Video ? "video/mp4" :
                                                    mt == mediaType.Quicktime ? "video/quicktime" : "application/pdf");
                                                xml.WriteEndElement();
                                                xml.WriteElementString("category", "Podcasts");
                                                xml.WriteElementString("itunes:author", article.get("publishAuthorName").ToString());
                                                xml.WriteElementString("itunes:subtitle", article.get("title").ToString());
                                                xml.WriteElementString("itunes:summary", article.get("summary").ToString());
                                                xml.WriteElementString("itunes:explicit", "No");
                                                xml.WriteElementString("itunes:duration", calcDuration(file.get("size").ToString()));
                                                xml.WriteElementString("itunes:keywords", article.get("keyword").ToString());
                                                xml.WriteEndElement();
                                            }

                                        }
                                    }
                                }
                                
                            }
                        }
                    xml.WriteEndElement();
                    xml.WriteEndElement();
                    xml.Close();
                    s.Close();
                    rssXml = s.ToString();
                }
                return rssXml;
            }
            catch (Exception e)
            {
                throw e;
            }
		}
        private mediaType calcMediaType(string path)
        {
            if (path == null || path.Length == 0 || !path.Contains("."))
                return mediaType.NotSupported;
            string ext = path.Substring(path.LastIndexOf(".")).ToLower();
            if (ext.Equals(".mp3"))
                return mediaType.Audio;
            else if (ext.Equals(".mp4") || ext.Equals(".wmv"))
                return mediaType.Video;
            else if(ext.Equals(".mov"))
                return mediaType.Quicktime;
            else if(ext.Equals(".pdf"))
                return mediaType.PDF;
            else
                return mediaType.NotSupported;
        }
        private enum mediaType
        {
            Audio,
            Video,
            PDF,
            Quicktime,
            NotSupported
        }
        private string calcLength(string val)
        {
            if (val == null && val.Length == 0)
                return "0";
            string[] value = val.Split(' ');
            if (value.Length == 1)
                return value[0];

            int multiplier = 1048576;
            
            if (value[1].ToLower().Trim().Equals("kb"))
                multiplier = 1024;

            double len = Convert.ToDouble(value[0]);
            return (len * multiplier).ToString();
        }

        private string calcDuration(string strval)
        {
            if (strval == null && strval.Length == 0)
                return "0";
            string[] value = strval.Split(' ');
            //todo: validate files that are less than 1mb
            double d = Convert.ToDouble(value[0]);
            return d.ToString("00.00").Replace('.', ':');

        }
        /// <summary> 
        /// Converts a regular DateTime to a RFC822 date string. 
        /// http://madskristensen.net/post/Convert-a-date-to-the-RFC822-standard-for-use-in-RSS-feeds.aspx
        /// </summary> 
        /// <returns>The specified date formatted as a RFC822 date string.</returns> 

        private static string GetRFC822Date(DateTime date)
        {
            if (date == null || date.Equals(DateTime.MinValue))
                date = DateTime.Now;
            int offset = TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now).Hours;
            string timeZone = "+" + offset.ToString().PadLeft(2, '0');
            if (offset < 0)
            {
                int i = offset * -1;
                timeZone = "-" + i.ToString().PadLeft(2, '0');
            }
            return date.ToString("ddd, dd MMM yyyy HH:mm:ss " + timeZone.PadRight(5, '0'));
        } 

	}
}
